package pr.lanmu.config.util;

import cn.hutool.json.JSONUtil;
import lombok.SneakyThrows;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import pr.lanmu.common.po.Cron;
import pr.lanmu.dao.CronDao;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.stream.Collectors;

@Component
@SuppressWarnings("all")
public class QuartzJob implements Job {

    private static CronDao cronDao;
    private static String url;
    private final Scheduler scheduler;
    private final String GROUP = "cron";
    private final String DATA = "data";

    public QuartzJob() {
        try {
            scheduler = StdSchedulerFactory.getDefaultScheduler();
            scheduler.start();
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }

    @Autowired(required = false)
    public void setCronDao(CronDao cronDao) {
        QuartzJob.cronDao = cronDao;
    }

    @Value("${third.self}")
    public void setUrl(String self) {
        QuartzJob.url = self;
    }

    /**
     * 加载定时任务
     *
     * @param cron .
     */
    @SneakyThrows
    public void loadJob(Cron cron) {
        if (cron == null || !cron.getState()) return;
        String tag = cron.getTag();
        String code = cron.getCode();
        String name = cron.getName();
        TriggerKey triggerKey = TriggerKey.triggerKey(tag, GROUP);
        Trigger trigger = scheduler.getTrigger(triggerKey);
        if (trigger == null) {
            JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
                    .withIdentity(tag, GROUP)
                    .withDescription(name)
                    .usingJobData(DATA, JSONUtil.toJsonStr(cron))
                    .build();
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(code)
                    // 定时任务错过处理策略，避免resume时再次执行trigger
                    .withMisfireHandlingInstructionDoNothing();
            trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(code)
                    .withMisfireHandlingInstructionDoNothing();
            trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    }

    /**
     * 移除定时任务
     *
     * @param cron .
     */
    public void removeJob(Cron cron) {
        if (cron == null || !cron.getState()) return;
        String tag = cron.getTag();
        TriggerKey triggerKey = TriggerKey.triggerKey(tag, GROUP);
        try {
            scheduler.pauseTrigger(triggerKey);
            scheduler.unscheduleJob(triggerKey);
            scheduler.deleteJob(JobKey.jobKey(tag, GROUP));
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void execute(JobExecutionContext context) {
        String data = context.getJobDetail().getJobDataMap().getString(DATA);
        Cron cron = JSONUtil.toBean(data, Cron.class);
        Log.info("处理定时任务：{}", cron.getName());
        WebClient webClient = WebClient.builder().baseUrl(QuartzJob.url).build();
        webClient.post()
                .uri(cron.getUrl())
                .header("Content-Type", "application/json")
                .header("token", cron.getToken())
                .bodyValue(cron.getJson() == null ? "{}" : cron.getJson())
                .retrieve()
                .bodyToMono(String.class)
                .subscribe(res -> {
                    Log.info("处理定时任务：{}，成功", cron.getName());
                    //version自增，使用sql自增
                    cronDao.plusVersion(cron.getId());
                }, err -> Log.error("处理定时任务：{}，失败", cron.getName()));
    }

    /**
     * 获取全部装载的定时任务
     *
     * @return .
     */
    public List<Cron> getAllTimer() {
        try {
            return scheduler.getJobKeys(GroupMatcher.anyGroup())
                    .stream()
                    .map(e -> {
                        try {
                            JobDetail jobDetail = scheduler.getJobDetail(e);
                            String value = jobDetail.getJobDataMap()
                                    .getString(DATA);
                            return JSONUtil.toBean(value, Cron.class)
                                    .setCode(((CronTriggerImpl) scheduler.getTrigger(TriggerKey.triggerKey(e.getName(), e.getGroup()))).getCronExpression());
                        } catch (SchedulerException ex) {
                            throw new RuntimeException(ex);
                        }
                    })
                    .collect(Collectors.toList());
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 时间戳转cron表达式
     *
     * @param time .
     * @return .
     */
    public String getCron(Long time) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault());
        return String.format("%d %d %d %d %d ? %d",
                localDateTime.getSecond(),
                localDateTime.getMinute(),
                localDateTime.getHour(),
                localDateTime.getDayOfMonth(),
                localDateTime.getMonthValue(),
                localDateTime.getYear());
    }
}